////
/// @group box
////

@use "sass:map";
@use "../utils";
@use "../spacing";
@use "../media-queries/media-queries";

/// Set to `true` to disable all the styles
/// @type Boolean
$disable-everything: false !default;

/// Set to `true` to disable all the styles for stacking items using
/// `flex-direction: column` and `flex-direction: column-reverse`.
/// @type Boolean
$disable-stacked: false !default;

/// Set to `true` to disable all the `flex-direction: row-reverse` and
/// `flex-direction: column-reverse` styles.
/// @type Boolean
$disable-reversed: false !default;

/// Set to `true` to disable the css grid behavior for the box.
/// @type Boolean
$disable-grid: false !default;

/// Set to `true` to disable the styles for the auto grid columns behavior and
/// anything related to the `$item-min-size`.
/// @type Boolean
$disable-grid-columns: false !default;

/// Set to `true` to disable the styles for the `grid-auto-rows` behavior.
/// @type Boolean
$disable-grid-auto-rows: false !default;

/// Set to `true` to disable the styles for the phone columns variable.
/// @type Boolean
$disable-phone-columns: false !default;

/// Set to `true` to disable the styles for the tablet columns variable.
/// @type Boolean
$disable-tablet-columns: false !default;

/// Set to `true` to disable the styles for the desktop columns variable.
/// @type Boolean
$disable-desktop-columns: false !default;

/// Set to `true` to disable the styles for the large desktop columns variable.
/// @type Boolean
$disable-large-desktop-columns: false !default;

/// Set to `true` to disable the styles for the phone grid min item variable.
/// @type Boolean
$disable-phone-min-item-size: false !default;

/// Set to `true` to disable the styles for the tablet grid min item variable.
/// @type Boolean
$disable-tablet-min-item-size: false !default;

/// Set to `true` to disable the styles for the desktop grid min item size
/// variable.
/// @type Boolean
$disable-desktop-min-item-size: false !default;

/// Set to `true` to disable the styles for the large desktop grid min item
/// size variable
/// @type Boolean
$disable-large-desktop-min-item-size: false !default;

/// This is the default `align-items` property value to set if you don't like
/// everything being centered by default. This value is always overridable using
/// the `alignItems` prop on the `Box` component.
/// @type String
$default-align-items: center !default;

/// The additional class names to create for the `align-items` property. It
/// creates classes like:
///
/// ```scss
/// .rmd-box--align-start {
///   align-items: start;
/// }
/// ```
///
/// @type List
$align-items: (start end stretch) !default;

/// The additional class names to create for the `justify-content` property. It
/// creates classes like:
///
/// ```scss
/// .rmd-box--justify-start {
///   justify-content: start;
/// }
/// ```
///
/// @type List
$justify-content: (
  start center end stretch space-around space-between space-evenly
) !default;

/// The default flex and grid gap to apply between each item
/// @type Number
$gap: spacing.get-var(md) !default;

/// The default padding to apply to the container.
/// @type Number
$padding: $gap !default;

/// The default minimum size for each grid item
/// @type Number
$item-min-size: 8rem !default;

/// This is used to create custom reusable grid configurations throughout the
/// app. The custom grid can be used through the `gridName` prop.
///
/// @example scss - Simple Example<!-- no-compile -->
///   @use "@react-md/core" with (
///     $box-grids: (
///       small: (
///         min: 5rem
///       ),
///       medium: (
///         min: 7rem,
///         padding: 2rem,
///         gap: 0.5rem
///       )
///     )
///   );
///
/// @type Map
$grids: () !default;

/// @type Map
/// @access private
$_breakpoints: (
  phone: (
    columns: $disable-phone-columns,
    size: $disable-phone-min-item-size,
  ),
  tablet: (
    columns: $disable-tablet-columns,
    size: $disable-tablet-min-item-size,
  ),
  desktop: (
    columns: $disable-desktop-columns,
    size: $disable-desktop-min-item-size,
  ),
  large-desktop: (
    columns: $disable-large-desktop-columns,
    size: $disable-large-desktop-min-item-size,
  ),
);

/// @type Boolean
/// @access private
$_disable-media-queries: $disable-phone-columns and $disable-tablet-columns and
  $disable-desktop-columns and $disable-large-desktop-columns;

/// The available configurable css variables and mostly used internally for the
/// `get-var`, `set-var`, and `use-var` utils.
/// @type List
$variables: (
  gap,
  column-gap,
  row-gap,
  padding,
  padding-v,
  padding-h,
  item-min-size,
  item-min-height,
  columns,
  row-max-height,
  auto-rows-height,
  fallback-media-columns,
  phone-columns,
  phone-item-min-size,
  phone-item-min-height,
  tablet-columns,
  tablet-item-min-size,
  tablet-item-min-height,
  desktop-columns,
  desktop-item-min-size,
  desktop-item-min-height,
  large-desktop-columns,
  large-desktop-item-min-size,
  large-desktop-item-min-height
);

/// @param {String} name - The supported variable name
/// @param {any} fallback [null] - An optional fallback value
/// @returns {String} a `var()` statement
@function get-var($name, $fallback: null) {
  $var: utils.get-var-name($variables, $name, "box");

  @if $fallback {
    @return var(#{$var}, #{$fallback});
  }

  @return var(#{$var});
}

/// @param {String} name - The supported variable name
/// @param {any} value - The value to set the variable to. Supports `null` which
/// will just be a no-op.
@mixin set-var($name, $value) {
  @if $value {
    #{utils.get-var-name($variables, $name, "box")}: #{$value};
  }
}

/// @param {String} property - The css property to apply the variable to
/// @param {String} name [$property] - The supported variable name
/// @param {any} fallback [null] - An optional fallback value if the variable
/// has not been set
@mixin use-var($property, $name: $property, $fallback: null) {
  // these variables define another fallback and are not set at the root so
  // they can easily be overridden. if they have a default value at the root,
  // that root value will be used instead of the lowest defined override.
  //
  // i.e.
  // ```scss
  //
  // :root {
  //   --rmd-box-padding: 3rem;
  //   --rmd-box-padding-v: var(--rmd-box-padding);
  //   --rmd-box-padding-h: var(--rmd-box-padding);
  // }
  //
  // .some-child {
  //   --rmd-box-padding: 1rem;
  //
  // }
  // ```
  //
  // `--rmd-box-padding-v` and `--rmd-box-padding-h` would still have a value
  // of `1rem`
  @if not $fallback {
    @if $name == padding-v or $name == padding-h {
      $fallback: get-var(padding);
    } @else if $name == column-gap or $name == row-gap {
      $fallback: get-var(gap);
    } @else if
      $name ==
      phone-columns or
      $name ==
      tablet-columns or
      $name ==
      desktop-columns or
      $name ==
      large-desktop-columns
    {
      $fallback: get-var(columns);
    } @else if
      $name ==
      phone-item-min-height or
      $name ==
      tablet-item-min-height or
      $name ==
      desktop-item-min-height or
      $name ==
      large-desktop-item-min-height
    {
      $fallback: get-var(item-min-height);
    } @else if
      $name ==
      phone-size or
      $name ==
      tablet-size or
      $name ==
      desktop-size or
      $name ==
      large-desktop-size
    {
      $fallback: get-var(size);
    } @else if $name == gap {
      $fallback: $gap;
    } @else if $name == padding {
      $fallback: $padding;
    }
  }

  #{$property}: get-var($name, $fallback);
}

/// Generates the styles for a custom grid by updating the css variables.
///
/// @example scss - Simple Example
///   .container {
///     @include custom-grid(
///       $min: 20rem,
///       $padding: 0.25rem,
///       $gap: 0.725rem
///     );
///   }
///
/// @param {Number} min [null] - An optional item min size to override
/// @param {Number} padding [null] - An optional container padding to override
/// @param {Number} gap [null] - An optional gap between each item to override
@mixin custom-grid($min: null, $padding: null, $gap: null) {
  @if $min {
    @include set-var(item-min-size, $min);
  }

  @if $padding {
    @include set-var(padding, $padding);
  }

  @if $gap {
    @include set-var(gap, $gap);
  }
}

/// Generates the styles for a custom grid by updating the css variables.
///
/// @example scss - Simple Example
///   .container {
///     @include custom-grid-class(
///       $selector: '&--custom',
///       $min: 20rem,
///       $padding: 0.25rem,
///       $gap: 0.725rem
///     );
///   }
///
/// @param {String} selector - The selector to use to update the grid styles.
/// @param {Number} min [null] - An optional item min size to override
/// @param {Number} padding [null] - An optional container padding to override
/// @param {Number} gap [null] - An optional gap between each item to override
@mixin custom-grid-class($selector, $min: null, $padding: null, $gap: null) {
  #{$selector} {
    @include custom-grid($min, $padding, $gap);
  }
}

/// Conditionally applies the css variables based on feature flags
@mixin variables {
  @if not $disable-everything {
    @include set-var(item-min-size, $item-min-size);
    @include set-var(columns, auto-fit);
  }
}

/// Generates all the styles based on feature flags.
///
/// @param {Boolean} disable-layer [false] - Set this to `true` to disable the
/// layer behavior
@mixin styles($disable-layer: false) {
  @if not $disable-everything {
    @include utils.optional-layer(box, $disable-layer) {
      .rmd-box {
        align-items: $default-align-items;
        display: flex;

        &--gap {
          @include use-var(gap);
        }

        &--gap-h {
          @include use-var(column-gap);
        }

        &--gap-v {
          @include use-var(row-gap);
        }

        &--padded {
          padding: get-var(padding-v, get-var(padding, $padding))
            get-var(padding-h, get-var(padding, $padding));
        }

        &--wrap {
          flex-wrap: wrap;
        }

        &--full-width {
          width: 100%;
        }

        @if not $disable-stacked {
          &--column {
            flex-direction: column;
          }
        }

        @if not $disable-reversed {
          &--reverse {
            flex-direction: row-reverse;
          }

          @if not $disable-stacked {
            &--column-reverse {
              flex-direction: column-reverse;
            }
          }
        }

        @each $value in $align-items {
          &--align-#{$value} {
            align-items: $value;
          }
        }

        @each $value in $justify-content {
          &--justify-#{utils.str-replace($value, "space-", "")} {
            justify-content: $value;
          }
        }

        @if not $disable-grid {
          &--grid {
            @if not $_disable-media-queries {
              @include set-var(fallback-media-columns, auto-fit);
            }

            display: grid;
            grid-template-columns: repeat(
              get-var(columns),
              minmax(get-var(item-min-size), 1fr)
            );
          }

          &--grid-fill {
            @if not $_disable-media-queries {
              @include set-var(fallback-media-columns, auto-fill);
            }
            @include set-var(columns, auto-fill);
          }

          @if not $disable-grid-columns {
            &--grid-size {
              @include set-var(item-min-size, 0);
            }
          }

          @each $media, $feature-flags in $_breakpoints {
            @include media-queries.media-or-media-query($media) {
              $disable-columns: map.get($feature-flags, columns);
              $disable-size: map.get($feature-flags, size);
              @if not $disable-columns {
                &--grid-#{$media} {
                  @include set-var(
                    columns,
                    get-var(
                      $media + "-columns",
                      get-var(fallback-media-columns)
                    )
                  );
                }
              }

              @if not $disable-size {
                &--grid-#{$media}-size {
                  @include set-var(
                    item-min-size,
                    get-var($media + "-item-min-size", 0)
                  );
                }
              }
            }
          }

          @if not $disable-grid-auto-rows {
            &--grid-auto-rows {
              grid-auto-rows: minmax(
                get-var(item-min-height, get-var(item-min-size)),
                get-var(row-max-height, 1fr)
              );
              max-height: get-var(auto-rows-height, 100%);
              overflow: auto;
            }
          }

          @each $name, $values in $grids {
            @include custom-grid-class(
              "&--#{$name}",
              $min: map.get($values, min),
              $gap: map.get($values, gap),
              $padding: map.get($values, padding)
            );
          }
        }
      }
    }
  }
}
